package com.btpj.jetpack.base

import android.content.Context
import android.os.Bundle
import androidx.annotation.IdRes
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.Navigator
import java.util.ArrayDeque

@Navigator.Name("FixFragment")
class FixFragmentNavigator(
    private val context: Context,
    private val manager: FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(
        destination: Destination,
        args: Bundle?,
        navOptions: NavOptions?,
        navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        if (manager.isStateSaved) return null
        var className = destination.className
        if (className[0] == '.') {
            className = context.packageName + className
        }

        val fragmentTransition = manager.beginTransaction()
        var enterAnim = navOptions?.enterAnim ?: -1
        var exitAnim = navOptions?.exitAnim ?: -1
        var popEnterAnim = navOptions?.popEnterAnim ?: -1
        var popExitAnim = navOptions?.popExitAnim ?: -1
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
            enterAnim = if (enterAnim != -1) enterAnim else 0
            exitAnim = if (exitAnim != -1) exitAnim else 0
            popEnterAnim = if (popEnterAnim != -1) popEnterAnim else 0
            popExitAnim = if (popExitAnim != -1) popExitAnim else 0
            fragmentTransition.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
        }

        /**
         * 1、先查询当前显示的fragment 不为空则将其hide
         * 2、根据tag查询当前添加的fragment是否不为null，不为null则将其直接show
         * 3、为null则通过instantiateFragment方法创建fragment实例
         * 4、将创建的实例添加在事务中
         */
        val fragment = manager.primaryNavigationFragment //当前显示的fragment
        if (fragment != null) {
            fragmentTransition.hide(fragment)
        }

        var frag: Fragment?
        val tag = destination.id.toString()
        frag = manager.findFragmentByTag(tag)
        if (frag != null) {
            fragmentTransition.show(frag)
        } else {
            frag = instantiateFragment(context, manager, className, args)
            frag.arguments = args
            fragmentTransition.add(containerId, frag, tag)
        }

        fragmentTransition.setPrimaryNavigationFragment(frag)
        @IdRes val destId = destination.id

        /**
         *  通过反射的方式获取 mBackStack
         */
        val mBackStack: ArrayDeque<Int>

        val field = FragmentNavigator::class.java.getDeclaredField("mBackStack")
        field.isAccessible = true
        mBackStack = field.get(this) as ArrayDeque<Int>


        val initialNavigation = mBackStack.isEmpty()
        val isSingleTopReplacement = (navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId)

        val isAdded: Boolean
        when {
            initialNavigation -> {
                isAdded = true
            }
            isSingleTopReplacement -> {
                // Single Top means we only want one instance on the back stack
                if (mBackStack.size > 1) {
                    // If the Fragment to be replaced is on the FragmentManager's
                    // back stack, a simple replace() isn't enough so we
                    // remove it from the back stack and put our replacement
                    // on the back stack in its place
                    manager.popBackStack(
                        generateBackStackName(mBackStack.size, mBackStack.peekLast()),
                        FragmentManager.POP_BACK_STACK_INCLUSIVE
                    )
                    fragmentTransition.addToBackStack(
                        generateBackStackName(
                            mBackStack.size,
                            destId
                        )
                    )
                }
                isAdded = false
            }
            else -> {
                fragmentTransition.addToBackStack(
                    generateBackStackName(
                        mBackStack.size + 1,
                        destId
                    )
                )
                isAdded = true
            }
        }
        if (navigatorExtras is Extras) {
            val extras = navigatorExtras as Extras?
            for ((key, value) in extras!!.sharedElements) {
                fragmentTransition.addSharedElement(key, value)
            }
        }
        fragmentTransition.setReorderingAllowed(true)
        fragmentTransition.commit()
        // The commit succeeded, update our view of the world
        return if (isAdded) {
            mBackStack.add(destId)
            destination
        } else {
            null
        }
    }

    /**
     * 在父类是 private的  直接定义一个方法即可
     */
    private fun generateBackStackName(backIndex: Int, destId: Int): String {
        return "$backIndex - $destId"
    }
}